/*
 * Copyright (c) 2024 iSOFT INFRASTRUCTURE SOFTWARE CO., LTD.
 * easyAda is licensed under Mulan PubL v2.
 * You can use this software according to the terms and conditions of the Mulan PubL v2.
 * You may obtain a copy of Mulan PubL v2 at:
 *          http://license.coscl.org.cn/MulanPubL-2.0
 * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
 * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
 * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
 * See the Mulan PubL v2 for more details.
 */


#include <core/fault.h>
#include <core/schedule.h>
#include <core/task.h>
#include <caps/endpoint.h>
#include <uapi/errors.h>
#include <uapi/ipcbuffer.h>

static void fault_backup(void *task)
{
    unsigned long retcode = task_get_retcode(task);
    ipcbuffer_t  *buf     = task_get_ipcbuffer(task);

    buf->msgs.backup.retcode = retcode;
    /* PRQA S 2840,2845 1 */
    (void)memmove(&buf->msgs.backup.cptr, &buf->msgs.cptr, sizeof(fault_backup_t) - sizeof(unsigned long));
}

static void fault_recovery(void *task)
{
    ipcbuffer_t *buf = task_get_ipcbuffer(task);
    task_set_retcode(task, buf->msgs.backup.retcode);
    /* PRQA S 2840,2845 1 */
    (void)memmove(&buf->msgs.cptr, &buf->msgs.backup.cptr, sizeof(fault_backup_t) - sizeof(unsigned long));
}

void fault_callback(void *task, unsigned result)
{
    task_clear_flags(task, TASK_FBIT_REPORT);

    if ((task_get_fault(task) != (int)FAULT_CHILD_EXIT) && (result == (long)ESUCCESS)) {
        fault_recovery(task);
        schedule_attach(task);
    }
}

void fault_report(void *_task, int type, unsigned long info0, unsigned long info1)
{
    endpoint_t  *ep;
    tcb_t       *task  = _task;
    unsigned int state = task_get_state(task);

    assert(tcb_is_valid(task));

    task_set_fault(task, type);
    ep = task_get_faulthandler(task);
    if (type != (int)FAULT_CHILD_EXIT) {
        fault_backup(task);
    }

    task_set_flags(task, TASK_FBIT_REPORT);
    ipcbuffer_set_msgs(task_get_ipcbuffer(task), 2, task_get_id(task));

    endpoint_call(ep, msgtag_make(3, (unsigned short)type, 0), info0, info1, 0, 0, 0, 0, task);

    task_set_state(task, state);
}

void hardfault_handler(int type, unsigned long status, unsigned long addr)
{
    tcb_t *cur = current();

    tcb_get(cur);

    if (type == (int)FAULT_UNRECOVERY) {
        task_set_state(cur, TASK_INACTIVE);
        task_prepare_exit(cur);
    }
    fault_report(cur, type, status, addr);

    dprintf("hardfault_handler!\n");
    (void)tcb_put(cur);
    schedule();
}
